﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LibMinecraft.Model;
using System.Net;
using System.IO;
using System.Threading.Tasks;
using PacketQueue = System.Collections.Concurrent.ConcurrentQueue<byte[]>;
using System.Diagnostics;
using System.Globalization;

namespace PacketPasser
{
    public class MinecraftProxy
    {
        readonly PacketQueue _packetBuffer =
            new PacketQueue();

        readonly PacketQueue _clientBuffer = new PacketQueue();
        public int FailedProcessingCount { get { return _clientBuffer.Count; } }
        public bool TryGetFailedProcessingPacket(out byte[] packet)
        {
            return _clientBuffer.TryDequeue(out packet);
        }
        public int PacketQueueLength { get { return _packetBuffer.Count; } }
        bool _pauseProcessing;
        private readonly Task _processor;

        public MinecraftProxy()
        {
            _processor = new Task(ProcessQueue);
            _processor.Start();
        }

        internal void ProcessPacket(byte[] packetCopy)
        {
            PacketID packetID = (PacketID)packetCopy[0];
            if (packetID == PacketID.ChatUpdate)
            {
               var output= ProcessChat(packetCopy, packetCopy.Length);
               if (output != null)
                   _clientBuffer.Enqueue(packetCopy);
               return;
            }

            using (var stream = new System.IO.MemoryStream(packetCopy))
            {
               stream.ReadByte();
                //Debug.WriteLine("Processing packet:" + packetID.ToString());
                if (packetID == PacketID.LoginRequest)
                    ProcessLoginData(stream);
                

            }

        }

        internal static void ProcessLoginData(Stream stream)
        {
            Debug.Assert(stream.Length > stream.Position);
            Func<string, string> attemptRead = (title) =>
                                                   {
                                                       try
                                                       {
                                                           return title + ":" + stream.ReadString();
                                                       }
                                                       catch (OverflowException)
                                                       {
                                                           Debug.WriteLine("Failed to read " + title);
                                                           return "Failed to read " + title;

                                                       }
                                                   };
            var playerIDRaw = stream.ReadInt32();

            Console.WriteLine("PlayerID:" + IPAddress.HostToNetworkOrder(playerIDRaw));

            var unknown = stream.ReadString();
            //Console.WriteLine("?" + );
            Console.WriteLine("seed:" + stream.ReadInt64());
            Console.WriteLine(attemptRead("WorldGen"));

            Console.WriteLine("Game Mode:" + (LibMinecraft.Model.GameMode)stream.ReadInt32());
            Console.WriteLine("Dimension:" + (LibMinecraft.Model.Dimension)(sbyte)stream.ReadByte());
            Console.WriteLine("Difficulty:" + stream.ReadByte());
            Console.WriteLine("World Height:" + stream.ReadByte());
            Console.WriteLine("Max players:" + stream.ReadByte());
            stream.Flush();
        }

        const char chatColoring = '§';

        private static ConsoleColor[] colors = new[]
                                                   {
                                                       ConsoleColor.White,
                                                       ConsoleColor.DarkBlue,
                                                       ConsoleColor.DarkGreen,
                                                       ConsoleColor.DarkCyan,
                                                       ConsoleColor.DarkRed,
                                                       ConsoleColor.Magenta,
                                                       ConsoleColor.DarkYellow,
                                                       ConsoleColor.Gray,
                                                       ConsoleColor.DarkGray,
                                                       ConsoleColor.Blue,
                                                       ConsoleColor.Green,
                                                       ConsoleColor.Cyan,
                                                       ConsoleColor.Red,
                                                       ConsoleColor.Red,
                                                       ConsoleColor.Yellow,
                                                       ConsoleColor.White

                                                   };

        static string CapsFilter(string input)
        {
            if (input.Count(c => char.IsUpper(c)) > 6)
                return input.ToLower();
            return input;
        }


        public static string Username { get; set; }

        static void WriteOut(string message,Action<string> output,Action<string> outputLine, Action<ConsoleColor> colorChange=null, Func<ConsoleColor> getColor=null)
        {
            ConsoleColor old=ConsoleColor.White;
            if(getColor!=null)
             old = getColor();
            foreach (var colored in message.PrependOrSelf(chatColoring.ToString(), chatColoring + "0").Split(chatColoring))
            {
                if (colored.IsNullOrEmpty() || colored.Length < 2)
                    continue;
                int colorCode = 0;
                if (!int.TryParse(colored.Remove(1), NumberStyles.HexNumber, null, out colorCode) || colorCode < 0 || colorCode >= colors.Length)
                {
                    if (colorChange != null)
                        colorChange(old);
                    Console.Write(colored.Substring(1));
                    continue;
                }
                if (colorCode >= 0 && colorCode < colors.Length)
                {
                    if(colorChange!=null)
                    colorChange(colors[colorCode]);
                    Console.Write(colored.Substring(1));
                }


            }
            if(colorChange!=null)
            colorChange(old);
            Console.WriteLine();
        }
        static void WriteConsole(string input)
        {
            WriteOut(input, Console.Write, Console.WriteLine, color => Console.ForegroundColor = color, () => { return Console.ForegroundColor; });

        }
        
        static void WriteDebug(string input)
        {
            WriteOut(input,text=> Debug.Write(text),text=> Debug.WriteLine(text));

        }
        static byte[] ProcessChat(byte[] packet, int length)
        {
            if (packet == null || packet.Length < 2)
                return packet;
            
            var packetID = packet[0]; //ignore packetID
            Debug.Assert((int)PacketID.ChatUpdate == packetID);
            string result;
            try
            {

                result = packet.ReadString(1);
                Debug.WriteLine("Chat packet recieved, length:" + result.Length);
            }
            catch (OverflowException oEx)
            {

                Console.WriteLine("Failed to read chat:" + oEx.Message);
                return packet;
            }
            var message = CapsFilter(result);


           
            
            
            if (!message.Contains(chatColoring))
            {
                
                Console.WriteLine(message);
                //packet = null;
                return null;
            }

            WriteDebug(message);
            WriteConsole(message);

            //packet = null;
            return null;
        }
        void ProcessQueue()
        {
            while (true)
            {
                if (_packetBuffer.IsEmpty == false)
                {
                    byte[] packet;

                    if (_packetBuffer.TryDequeue(out packet))
                        ProcessPacket(packet);
                    else
                        System.Threading.Thread.Sleep(10);
                }
                System.Threading.Thread.Sleep(10);
            }

        }
        #region IProcessPackets Members

        public bool VisitPacket(byte[] packet, int length)
        {
            if (length == -1)
                return true;
            if (length < 1)
                return true;
            PacketID packetID = (PacketID)packet[0];
            if (packetID == PacketID.PluginMessage)
                Console.WriteLine("PluginMessage found");

            //Debug.WriteLine("VisitPacket:" + packetID.ToString());
            var copy = new byte[length];
            Buffer.BlockCopy(packet, 0, copy, 0, length);
            for (int i = 0; i < copy.Length; i++)
            {
                if (packet[i] != copy[i])
                    throw new InvalidOperationException();
            }

            _packetBuffer.Enqueue(copy);
            if (packetID == PacketID.ChatUpdate)
                return false; 
            return true;

        }

        #endregion
    }
}
